/******************************************************************************
 * $LastChangedDate: 2011-09-10 18:51:54 -0400 (Sat, 10 Sep 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Debug support (unused because my code is always perfect).
 *//*
 * LEGAL:   COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 ******************************************************************************/

#if DEBUG

#define BASE_DEBUG_CC 1
#if OS_APPLE
#   include <mach/mach_init.h>
#   include <mach/task.h>
#elif OS_UNIX
#   include <sstream>
#   include <sys/time.h>
#   include <time.h>
#   if __GLIBC__  // GNU Linux
#       include <malloc.h>
#   elif OS_FREEBSD
#       include <unistd.h>
#       include <sys/types.h>
#       include <sys/file.h>
#       include <sys/sysctl.h>
#       include <sys/user.h>
#       include <kvm.h>
#   endif
#endif
#include "base/module.hh"
#include "base/stream.hh"
#include "base/debug.hh"

namespace base {

#if DEBUG
// For a check when compiling tests which require DEBUG build.
bool BASE_DEBUG_BUILD = true;
#endif

/*****************************************************************************
 * Return total amount of bytes allocated.
 *****************************************************************************/
unsigned long
MemoryUsed( void )
{
#if __GLIBC__  // GNU Linux

    // Per delorie.com:
    // Example:
    // struct mallinfo info = mallinfo();
    // printf("Memory in use: %d bytes\n", info.usmblks + info.uordblks);
    // printf("Total heap size: %d bytes\n", info.arena);
    struct mallinfo meminfo;
    meminfo = mallinfo();
  //return meminfo.arena;
    return meminfo.usmblks + meminfo.uordblks;

#elif OS_APPLE

    // Use Mach functions.
    task_basic_info        machInfo  = { 0 };
    mach_port_t            machTask  = mach_task_self();
    mach_msg_type_number_t machCount = TASK_BASIC_INFO_COUNT;
    if ( task_info( machTask, TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&machInfo), &machCount ) == KERN_SUCCESS )
        return machInfo.resident_size;
    else
        return 0;  // error

#elif OS_FREEBSD

    // getrusage() doesn't work right on FreeBSD and anyway it has
    // a horrible convoluted interface for measuring memory.
    // kvm is reliable but does require linking with the kvm library.
    PERSISTENT kvm_t* kd = kvm_open( NULL, "/dev/null", NULL, O_RDONLY, "kvm_open" );  // open once
    if ( kd != NULL )
    {
        // Use FreeBSD kvm function to get the size of resident pages (RSS).
        int procCount = 0;
        struct kinfo_proc* kp = kvm_getprocs( kd, KERN_PROC_PID, getpid(), &procCount );  // do not free returned struct
        if ( (kp != NULL) and (procCount >= 1) )    // in case multiple threads have the same PID
            return kp->ki_rssize * getpagesize();   // success
    }
    return 0;  // failed

#else

    return 0;  // unsupported

#endif
}

/*****************************************************************************
 * Measure the amount of time a function takes.
 *****************************************************************************/
#if OS_UNIX
void
MeasureTime( void(*func)(long), long funcArg, const string& funcName )
{
    // ANSI C code that measures the time the process consumed in user-mode.
    // Excludes kernel-mode (doesn't count time spent in video driver).
    int clocks1 = clock();
    func( funcArg );  // call function to be measured
    int clocks2      = clock();
    int clocksPerSec = CLOCKS_PER_SEC;
    fp  secUser      = fp(clocks2 - clocks1) / fp(clocksPerSec);
    std::ostringstream oss;
    oss << "------------------------------------\n"
        << "function: " << funcName << "\n"
        << "secs (user) = " << secUser << "\n"
        << "------------------------------------\n";
    string result = oss.str();
    CDEBUG << result.c_str();
}
#endif // OS_UNIX

} // namespace base

#endif // DEBUG
